home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-04
/
ms_sh21s.zip
/
SH210
/
SRC
/
SH3.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-14
|
70KB
|
3,230 lines
/* MS-DOS SHELL - Parse Tree Executor
*
* MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
*
* This code is based on (in part) the shell program written by Charles
* Forsyth and is subject to the following copyright restrictions:
*
* 1. Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice is duplicated in the
* source form and the copyright notice in file sh6.c is displayed
* on entry to the program.
*
* 2. The sources (or parts thereof) or objects generated from the sources
* (or parts of sources) cannot be sold under any circumstances.
*
* $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh3.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
*
* $Log: sh3.c,v $
* Revision 2.5 1992/12/14 10:54:56 istewart
* BETA 215 Fixes and 2.1 Release
*
* Revision 2.4 1992/11/06 10:03:44 istewart
* 214 Beta test updates
*
* Revision 2.3 1992/09/03 18:54:45 istewart
* Beta 213 Updates
*
* Revision 2.2 1992/07/16 14:33:34 istewart
* Beta 212 Baseline
*
* Revision 2.1 1992/07/10 10:52:48 istewart
* 211 Beta updates
*
* Revision 2.0 1992/05/07 20:31:39 Ian_Stewartson
* MS-Shell 2.0 Baseline release
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <dirent.h>
#include <ctype.h>
#ifdef OS2
#define INCL_DOSSESMGR
#define INCL_DOSQUEUES
#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#include <os2.h>
#else
#include <dos.h>
#endif
#include "sh.h"
/*
* Save struct for Parameters ($1, $2 etc)
*/
typedef struct SaveParameters {
char **Array; /* The parameters */
int Count; /* Number of them */
} SaveParameters;
/* static Function and string declarations */
static int near ForkAndExecute (C_Op *, int, int, int, char **,
char **);
static bool near WriteToExtendedFile (int, char *);
static C_Op ** near SearchCasePatternMatch (C_Op *, char *);
static void near EchoCurrentCommand (char **);
static int near ExecuteProgram (char *, char **, char **, int);
static bool near CheckParameterLength (char **);
static void near SaveNumericParameters (char **, SaveParameters *);
static void near RestoreTheParameters (SaveParameters *);
static bool near ExecuteFunction (char **, int *, bool);
static void near PrintLoadError (char *);
#ifndef OS2
static bool near Get_EMS_Driver (void);
static bool near Get_XMS_Driver (void);
static bool near EMS_error (char *, int);
static int near EMS_Close (void);
static bool near XMS_error (char *, int);
static int near XMS_Close (void);
static int near SwapToDiskError (int, char *);
static int near SwapToMemory (int, char **);
static int near SpawnProcess (char **);
#endif
static char * near ConvertErrorNumber (void);
static int near BuildCommandLine (char *, char **, char **, int);
static int near StartTheProcess (char *, char **, char **, int);
static int near SetCommandReturnStatus (int);
static char * near GenerateFullExecutablePath (char *);
static char ** near FindNumberOfValues (char **, int *);
static int near ExecuteScriptFile (char *, char **, char **, int, bool);
static char * near BuildOS2String (char **, char);
static int near EnvironExecute (char **, int);
static int near LocalExecve (char **, char **, int);
static unsigned int near CheckForCommonOptions (LineFields *, int);
static char **ProcessSpaceInParameters (char **);
static int near CountDoubleQuotes (char *);
#ifdef OS2
static char *InsertCharacterAtStart (char *);
static int near StartTheSession (STARTDATA *, char *, char **, char **);
static int near OS2_DosExecProgram (int, char *, char **, char **,
unsigned int);
#endif
static char *AE2big = "arg/env list too big";
static char *RootDirectory = "x:\\";
/* Extended Command line processing file name */
static char *Extend_file = (char *)NULL;
static char *DoubleQuotes = "\"";
/* Swapping messages */
#ifndef OS2
static char *NoSwapFiles = "No Swap files\n";
static char *MS_emsg = "Warning: %s Error (%x)\n";
static char *MS_Space = "Warning: %s out of space\n";
static char *SwapFailed = "%s swap failed (%x)\n";
static char *Swap_File = (char *)NULL; /* Swap file */
#endif
/*
* OS2 load error mode
*/
#ifdef OS2
static char FailName[NAME_MAX + PATH_MAX + 3];
#endif
/*
* Common fields in EXTENDED_LINE file
*/
#define COMMON_FIELD_COUNT 5
static struct CommonFields {
char *Name;
unsigned int Flag;
} CommonFields [] = {
{ "switch", EP_CONVERT },
{ LIT_export, EP_EXPORT },
{ "noswap", EP_NOSWAP },
{ "noexpand", EP_NOEXPAND },
};
/*
* execute tree recursively
*/
int ExecuteParseTree (register C_Op *t, int StandardIN, int StandardOUT,
int Actions)
{
register C_Op **tp;
int Count;
int LocalPipeFP;
#ifdef OS2
int ReadPipeFP;
int WritePipeFP;
#endif
char *cp, **wp;
char **Local_Tword;
Break_C BreakContinue;
Break_C *S_RList; /* Save link pointers */
Break_C *S_BList;
Break_C *S_SList;
GetoptsIndex GetoptsSave;
int Local_depth; /* Save local values */
int Local_MemoryAreaLevel;
int RetVal = 0; /* Return value */
char *InputBuffer; /* Select input Buffer */
char *EndIB; /* End of buffer */
char *LastWord = null;
/* End of tree ? */
if (t == (C_Op *)NULL)
return 0;
/* Save original and Increment execute function recursive level */
Local_depth = Execute_stack_depth++;
/* Save original and increment area number */
Local_MemoryAreaLevel = MemoryAreaLevel++;
/* Save the exit points from SubShells, functions and for/whiles */
S_RList = Return_List;
S_BList = Break_List;
S_SList = SShell_List;
/* Expand any arguments */
wp = (char **)NULL;
if ((Local_Tword = t->words) != (char **)NULL)
{
if (t->type != TCOM)
wp = eval (Local_Tword, EXPAND_ALL & ~EXPAND_MOVE,
(struct ExecutableProcessing *)NULL);
else
{
struct ExecutableProcessing SaveValues;
wp = eval (Local_Tword, EXPAND_ALL, &SaveValues);
ExecProcessingMode = SaveValues;
}
}
/* Switch on tree node type */
switch (t->type)
{
case TFUNC: /* name () { list; } */
RetVal = SaveFunction (t) ? 0 : 1;
SetCommandReturnStatus (RetVal);
break;
/* In the case of a () command string, we need to save and restore the
* current environment, directory and traps (can't think of anything else).
* For any other, we just restore the current directory. Also, we don't
* want changes in the Variable list header saved for SubShells, because
* we are effectively back at execute depth zero.
*/
case TPAREN: /* () */
if ((RetVal = CreateGlobalVariableList (FLAGS_NONE)) == -1)
break;
/* Save Getopts pointers */
GetGetoptsValues (&GetoptsSave);
if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
{
Return_List = (Break_C *)NULL;
Break_List = (Break_C *)NULL;
BreakContinue.NextExitLevel = SShell_List;
SShell_List = &BreakContinue;
RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,
wp, &LastWord);
}
/* Restore the original environment */
else
RetVal = (int)GetVariableAsNumeric (StatusVariable);
SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
Return_List = S_RList;
Break_List = S_BList;
SShell_List = S_SList;
RestoreEnvironment (RetVal, Local_depth);
break;
/* After a normal command, we need to restore the original directory. Note
* that a cd will have updated the variable $~, so no problem
*/
case TCOM: /* A command process */
RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions, wp,
&LastWord);
RestoreEnvironment (RetVal, Local_depth);
/* Save last word if appropriate */
if (!(DisabledVariables & DISABLE_LASTWORD))
{
SetVariableFromString (LastWordVariable, LastWord);
SetVariableStatus (LastWordVariable, STATUS_EXPORT);
}
break;
case TPIPE: /* Pipe processing */
#ifdef OS2
/* Do we want to use real pipes under OS2? */
if (GlobalFlags & FLAGS_REALPIPES)
{
if (DosMakePipe ((PHFILE) &ReadPipeFP, (PHFILE) &WritePipeFP, 0))
break;
/* Remap the IO handler */
ReadPipeFP = ReMapIOHandler (ReadPipeFP);
WritePipeFP = ReMapIOHandler (WritePipeFP);
DosSetFHandState (ReadPipeFP, OPEN_FLAGS_NOINHERIT);
DosSetFHandState (WritePipeFP, OPEN_FLAGS_NOINHERIT);
/* Is this a foreground thingy? */
if (!(Actions & (EXEC_SPAWN_NOWAIT | EXEC_SPAWN_IGNOREWAIT)))
{
int WaitPid;
WaitPid = ExecuteParseTree (t->left, StandardIN,
WritePipeFP,
EXEC_SPAWN_IGNOREWAIT);
close (WritePipeFP);
RetVal = ExecuteParseTree (t->right, ReadPipeFP,
StandardOUT, Actions);
close (ReadPipeFP);
cwait (&WaitPid, WaitPid, WAIT_GRANDCHILD);
}
/* Background processing */
else
{
ExecuteParseTree (t->left, StandardIN, WritePipeFP,
EXEC_SPAWN_IGNOREWAIT);
close (WritePipeFP);
RetVal = ExecuteParseTree (t->right, ReadPipeFP,
StandardOUT, Actions);
close (ReadPipeFP);
}
break;
}
#endif
/* MSDOS or OS/2 without real pipes - use files. Safer */
if ((RetVal = OpenAPipe ()) < 0)
break;
/* Create pipe, execute command, reset pipe, execute the other side, close
* the pipe and fini
*/
LocalPipeFP = ReMapIOHandler (RetVal);
ExecuteParseTree (t->left, StandardIN, LocalPipeFP, 0);
lseek (LocalPipeFP, 0L, SEEK_SET);
RetVal = ExecuteParseTree (t->right, LocalPipeFP, StandardOUT, 0);
CloseThePipe (LocalPipeFP);
break;
case TLIST: /* Entries in a for statement */
ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT, 0);
break;
case TASYNC: /* Async - not supported */
#ifdef OS2
RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
EXEC_SPAWN_NOWAIT);
#else
RetVal = -1;
if (!FL_TEST ('w'))
PrintWarningMessage ("sh: Async commands not supported\n");
SetCommandReturnStatus (RetVal);
#endif
break;
case TOR: /* || and && */
case TAND:
RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
if ((t->right != (C_Op *)NULL) &&
((RetVal == 0) == (t->type == TAND)))
RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
0);
break;
/* for x do...done and for x in y do...done - find the start of the variables
* count the number.
*/
case TFOR:
case TSELECT:
wp = FindNumberOfValues (wp, &Count);
/* Set up a long jump return point before executing the for function so that
* the continue statement is executed, ie we reprocessor the for condition.
*/
while (RetVal = setjmp (BreakContinue.CurrentReturnPoint))
{
/* Restore the current stack level and clear out any I/O */
RestoreEnvironment (0, Local_depth + 1);
Return_List = S_RList;
SShell_List = S_SList;
/* If this is a break - clear the variable and terminate the while loop and
* switch statement
*/
if (RetVal == BC_BREAK)
break;
}
if (RetVal == BC_BREAK)
break;
/* Process the next entry - Add to the break/continue chain */
BreakContinue.NextExitLevel = Break_List;
Break_List = &BreakContinue;
/* Execute the command tree */
if (t->type == TFOR)
{
while (Count--)
{
SetVariableFromString (t->str, *wp++);
RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
0);
}
}
/* Select option */
else if (!Count)
/* SKIP */;
/* Get some memory for the select input buffer */
else if ((InputBuffer = AllocateMemoryCell (LINE_MAX))
== (char *)NULL)
{
ShellErrorMessage (Outofmemory1);
RetVal = -1;
}
/* Process the select command */
else
{
bool OutputList = TRUE;
EndIB = &InputBuffer[LINE_MAX - 2];
while (TRUE)
{
int ReadCount; /* Local counter */
int OnlyDigits; /* Only digits in string*/
/* Output list of words */
if (OutputList)
{
for (ReadCount = 0; ReadCount < Count; ReadCount++)
fprintf (stderr, "%d: %s\n", ReadCount,
wp[ReadCount]);
OutputList = FALSE;
}
/* Output prompt */
OutputUserPrompt (PS3);
OnlyDigits = 1;
/* Read in until end of line, file or a field separator is detected */
for (cp = InputBuffer; (cp < EndIB); cp++)
{
if (((ReadCount = read (STDIN_FILENO, cp, 1)) != 1) ||
(*cp == CHAR_NEW_LINE))
{
break;
}
OnlyDigits = OnlyDigits && isdigit (*cp);
}
*cp = 0;
/* Check for end of file */
if (ReadCount != 1)
break;
/* Check for empty line */
if (!strlen (InputBuffer))
{
OutputList = TRUE;
continue;
}
SetVariableFromString (LIT_REPLY, InputBuffer);
/* Check that OnlyDigits is a valid number in the select range */
if (OnlyDigits &&
((OnlyDigits = atoi (InputBuffer)) >= 0) &&
(OnlyDigits < Count))
SetVariableFromString (t->str, wp[OnlyDigits]);
else
SetVariableFromString (t->str, null);
RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
0);
}
}
/* Remove this tree from the break list */
Break_List = S_BList;
break;
/* While and Until function. Similar to the For function. Set up a
* long jump return point before executing the while function so that
* the continue statement is executed OK.
*/
case TWHILE: /* WHILE and UNTIL functions */
case TUNTIL:
while (RetVal = setjmp (BreakContinue.CurrentReturnPoint))
{
/* Restore the current stack level and clear out any I/O */
RestoreEnvironment (0, Local_depth + 1);
Return_List = S_RList;
SShell_List = S_SList;
/* If this is a break, terminate the while and switch statements */
if (RetVal == BC_BREAK)
break;
}
if (RetVal == BC_BREAK)
break;
/* Set up links */
BreakContinue.NextExitLevel = Break_List;
Break_List = &BreakContinue;
while ((ExecuteParseTree (t->left, StandardIN, StandardOUT, 0) == 0)
== (t->type == TWHILE))
RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
0);
Break_List = S_BList;
break;
case TIF: /* IF and ELSE IF functions */
case TELIF:
if (t->right != (C_Op *)NULL)
RetVal = ExecuteParseTree (!ExecuteParseTree (t->left,
StandardIN,
StandardOUT, 0)
? t->right->left : t->right->right,
StandardIN, StandardOUT, 0);
break;
case TCASE: /* CASE function */
if ((cp = evalstr (t->str, DOSUB | DOTRIM)) == (char *)NULL)
cp = null;
if ((tp = SearchCasePatternMatch (t->left, cp)) != (C_Op **)NULL)
RetVal = ExecuteParseTree (*tp, StandardIN, StandardOUT, 0);
break;
case TBRACE: /* {} statement */
if ((RetVal >= 0) && (t->left != (C_Op *)NULL))
RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
(Actions & EXEC_FUNCTION));
break;
}
/* Processing Completed - Restore environment */
t->words = Local_Tword;
Execute_stack_depth = Local_depth;
/* Remove unwanted malloced space */
FreeAllHereFiles (MemoryAreaLevel);
ReleaseMemoryArea (MemoryAreaLevel);
MemoryAreaLevel = Local_MemoryAreaLevel;
/* Check for traps */
if (t->type == TCOM)
{
RunTrapCommand (-1); /* Debug trap */
if (RetVal)
RunTrapCommand (-2); /* Err trap */
}
/* Interrupt traps */
if ((Count = InterruptTrapPending) != 0)
{
InterruptTrapPending = 0;
RunTrapCommand (Count);
}
/* Check for interrupts */
if (Interactive () && SW_intr)
{
CloseAllHandlers ();
TerminateCurrentEnvironment ();
}
return RetVal;
}
/*
* Restore the original directory
*/
void RestoreCurrentDirectory (char *path)
{
SetCurrentDrive (tolower(*path) - 'a' + 1);
if (chdir (&path[2]) != 0)
{
if (!FL_TEST ('w'))
fputs ("Warning: current directory reset to /\n", stderr);
chdir (DirectorySeparator);
GetCurrentDirectory ();
}
}
/*
* Ok - execute the program, resetting any I/O required
*/
static int near ForkAndExecute (register C_Op *t, int StandardIN,
int StandardOUT, int ForkAction, char **wp,
char **LastWord)
{
int RetVal = -1; /* Return value */
int (*shcom)(int, char **) = (int (*)())NULL;
char *cp;
IO_Actions **iopp;
char **owp = wp;
int builtin = 0; /* Builtin function */
int LS_depth = Execute_stack_depth;
bool CGVLCalled = FALSE;
bool InternalExec = FALSE;
if (t->type == TCOM)
{
/* Malloc failed somewhere - given up. Also clear the second time
* exit flag (if OS2) so that the 'jobs active' message is not displayed.
*/
if (wp == (char **)NULL)
{
#ifdef OS2
ExitWithJobsActive = FALSE;
#endif
return SetCommandReturnStatus (-1);
}
/* Skip over any assignments */
while ((cp = *wp++) != (char *)NULL)
continue;
cp = *wp;
/* strip all initial assignments not correct wrt PATH=yyy command etc */
if (FL_TEST ('x') ||
((CurrentFunction != (FunctionList *)NULL) &&
CurrentFunction->Traced))
EchoCurrentCommand (cp != (char *)NULL ? wp : owp);
/* Is it only an assignement? */
if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
{
while (((cp = *(owp++)) != (char *)NULL) &&
AssignVariableFromString (cp))
continue;
#ifdef OS2
ExitWithJobsActive = FALSE;
#endif
return SetCommandReturnStatus (0);
}
/* Check for built in commands */
else if (cp != (char *)NULL)
{
shcom = IsCommandBuiltIn (cp, &builtin);
InternalExec = (strcmp (cp, LIT_exec)) == 0 ? TRUE : FALSE;
/*
* Reset the ExitWithJobsActive flag to enable the 'jobs active' on exit
* message
*/
#ifdef OS2
if (strcmp (cp, LIT_exit))
ExitWithJobsActive = FALSE;
#endif
}
#ifdef OS2
else
ExitWithJobsActive = FALSE;
#endif
}
#ifdef OS2
else
ExitWithJobsActive = FALSE;
#endif
/* Unix fork simulation? */
t->words = wp;
/* If there is a command to execute or we are exec'ing and this is not a
* TPAREN, save the current environment
*/
if (t->type != TPAREN)
{
if (((owp[0] != (char *)NULL) &&
((builtin & BLT_SKIPENVIR) != BLT_SKIPENVIR)) ||
(ForkAction & EXEC_WITHOUT_FORK))
{
if (CreateGlobalVariableList (FLAGS_FUNCTION) == -1)
return -1;
CGVLCalled = TRUE;
}
/* Set up any variables. Note there is an assumption that
* AssignVariableFromString sets the equals sign to 0, hiding the value;
*/
while (((cp = *(owp++)) != (char *)NULL) &&
AssignVariableFromString (cp))
{
if (shcom == (int (*)())NULL)
SetVariableStatus (cp, STATUS_EXPORT | STATUS_LOCAL);
}
}
/* We cannot close the pipe, because once the exec/spawn has taken place
* the processing of the pipe is not yet complete.
*/
if (StandardIN != NOPIPE)
{
S_dup2 (StandardIN, STDIN_FILENO);
/*lseek (STDIN_FILENO, 0L, SEEK_SET);*/
}
if (StandardOUT != NOPIPE)
{
fflush (stdout);
S_dup2 (StandardOUT, STDOUT_FILENO);
lseek (STDOUT_FILENO, 0L, SEEK_END);
}
/* Set up any other IO required */
fflush (stdout);
fflush (stderr);
if ((iopp = t->ioact) != (IO_Actions **)NULL)
{
while (*iopp != (IO_Actions *)NULL)
{
if (SetUpIOHandlers (*iopp++, StandardIN, StandardOUT))
return RetVal;
}
}
/* All fids above 10 are autoclosed in the exec file because we have used
* the O_NOINHERIT flag. Note I patched open.obj to pass this flag to the
* open function.
*/
if (t->type == TPAREN)
return RestoreStandardIO (ExecuteParseTree (t->left, NOPIPE, NOPIPE, 0),
TRUE);
/* Are we just changing the I/O re-direction for the shell ? */
if (wp[0] == NULL)
{
if ((ForkAction & EXEC_WITHOUT_FORK) == 0)
RestoreStandardIO (0, TRUE);
return 0;
}
/*
* Find the end of the parameters and set up $_ environment variable
*/
owp = wp;
while (*owp != (char *)NULL)
owp++;
/* Move back to last parameter, and save it */
*LastWord = StringCopy (*(owp - 1));
/* No - Check for a function the program. At this point, we need to put
* in some processing for return.
*/
if (!(builtin & BLT_CURRENT) && ExecuteFunction (wp, &RetVal, CGVLCalled))
return RetVal;
/* Check for another drive or directory in the restricted shell */
if (anys (":/\\", wp[0]) && CheckForRestrictedShell (wp[0]))
return RestoreStandardIO (-1, TRUE);
/* A little cheat to allow us to use the same code to start OS/2 sessions
* as to load and execute a program
*/
#ifdef OS2
SessionControlBlock = (STARTDATA *)NULL;
#endif
/* Ok - execute the program */
if (!(builtin & BLT_CURRENT))
{
RetVal = EnvironExecute (wp, ForkAction);
if (ExecProcessingMode.Flags != EP_ENVIRON)
RetVal = LocalExecve (wp, BuildCommandEnvironment (), ForkAction);
}
/* If we didn't find it, check for internal command
*
* Note that the exec command is a special case
*/
if ((builtin & BLT_CURRENT) || ((RetVal == -1) && (errno == ENOENT)))
{
if (shcom != (int (*)())NULL)
{
if (InternalExec)
RetVal = doexec (t);
else
RetVal = (*shcom)(CountNumberArguments (t->words), t->words);
SetCommandReturnStatus (RetVal);
}
else
PrintLoadError (wp[0]);
}
return RestoreStandardIO (RetVal, TRUE);
}
/*
* Restore Local Environment
*/
void RestoreEnvironment (int retval, int stack)
{
Execute_stack_depth = stack;
DeleteGlobalVariableList ();
RestoreCurrentDirectory (CurrentDirectory->value);
RestoreStandardIO (SetCommandReturnStatus (retval), TRUE);
}
/*
* Set up I/O redirection. 0< 1> are ignored as required within pipelines.
*/
bool SetUpIOHandlers (register IO_Actions *iop, int pipein, int pipeout)
{
register int u;
char *cp, *msg;
if (iop->io_unit == IODEFAULT) /* take default */
iop->io_unit = (iop->io_flag & (IOREAD | IOHERE)) ? STDIN_FILENO
: STDOUT_FILENO;
/* Check for pipes */
if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
return FALSE;
if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
return FALSE;
msg = (iop->io_flag & (IOREAD | IOHERE)) ? "open" : "create";
if ((iop->io_flag & IOHERE) == 0)
{
if ((cp = evalstr (iop->io_name, DOSUB | DOTRIM)) == (char *)NULL)
return TRUE;
}
if (iop->io_flag & IODUP)
{
if ((cp[1]) || !isdigit (*cp) && *cp != '-')
{
ShellErrorMessage ("illegal >& argument (%s)", cp);
return TRUE;
}
if (*cp == '-')
iop->io_flag = IOCLOSE;
iop->io_flag &= ~(IOREAD | IOWRITE);
}
/*
* When writing to /dev/???, we have to cheat because MSDOS appears to
* have a problem with /dev/ files after find_first/find_next.
*/
if (((iop->io_flag & ~(IOXHERE | IOTHERE)) == IOWRITE) &&
(strnicmp (cp, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0))
iop->io_flag |= IOCAT;
/* Open the file in the appropriate mode */
switch (iop->io_flag & ~(IOXHERE | IOTHERE | IOCLOBBER))
{
case IOREAD: /* < */
u = S_open (FALSE, cp, O_RDONLY);
break;
case IOHERE: /* << */
u = OpenHereFile (iop->io_name, iop->io_flag & IOXHERE);
cp = "here file";
break;
case IOWRITE | IOREAD: /* <> */
if (CheckForRestrictedShell (cp))
return TRUE;
u = S_open (FALSE, cp, O_RDWR);
break;
case IOWRITE | IOCAT: /* >> */
if (CheckForRestrictedShell (cp))
return TRUE;
if ((u = S_open (FALSE, cp, O_WRONLY | O_TEXT)) >= 0)
{
lseek (u, 0L, SEEK_END);
break;
}
case IOWRITE: /* > */
if (CheckForRestrictedShell (cp))
return TRUE;
if ((GlobalFlags & FLAGS_NOCLOBER) &&
(!(iop->io_flag & IOCLOBBER)) &&
(access (CheckDOSFileName (cp), F_OK) == 0))
{
u = -1;
break;
}
u = S_open (FALSE, cp, O_CMASK, 0666);
break;
case IODUP: /* >& */
if (CheckForRestrictedShell (cp))
return TRUE;
u = S_dup2 (*cp - '0', iop->io_unit);
break;
case IOCLOSE: /* >- */
if ((iop->io_unit >= STDIN_FILENO) &&
(iop->io_unit <= STDERR_FILENO))
S_dup2 (-1, iop->io_unit);
S_close (iop->io_unit, TRUE);
return FALSE;
}
if (u < 0)
{
PrintWarningMessage (LIT_3Strings, cp, "cannot ", msg);
return TRUE;
}
else if (u != iop->io_unit)
{
S_dup2 (u, iop->io_unit);
S_close (u, TRUE);
}
return FALSE;
}
/*
* -x flag - echo command to be executed
*/
static void near EchoCurrentCommand (register char **wp)
{
register int i;
if ((CurrentFunction != (FunctionList *)NULL) && CurrentFunction->Traced)
fprintf (stderr, "%s:", CurrentFunction->tree->str);
fputs (GetVariableAsString (PS4, TRUE), stderr);
for (i = 0; wp[i] != (char *)NULL; i++)
{
if (i)
fputc (CHAR_SPACE, stderr);
fputs (wp[i], stderr);
}
fputc (CHAR_NEW_LINE, stderr);
}
/* Search the case patterns for the command set whose match pattern matches
* the case variable
*/
static C_Op ** near SearchCasePatternMatch (C_Op *t, char *w)
{
register C_Op *t1;
C_Op **tp;
register char **wp, *cp;
if (t == (C_Op *)NULL)
return (C_Op **)NULL;
if (t->type == TLIST)
{
if ((tp = SearchCasePatternMatch (t->left, w)) != (C_Op **)NULL)
return tp;
t1 = t->right; /* TPAT */
}
else
t1 = t;
for (wp = t1->words; *wp != (char *)NULL;)
{
if ((cp = evalstr (*(wp++), DOSUB)) &&
GeneralPatternMatch (w, cp, FALSE, (char **)NULL, GM_ALL))
return &t1->left;
}
return (C_Op **)NULL;
}
/*
* Set up the status on exit from a command
*/
static int near SetCommandReturnStatus (int s)
{
SetVariableFromNumeric (StatusVariable, (long)(ExitStatus = s));
return s;
}
/*
* Execute a command
*/
int ExecuteACommand (char **argv, int mode)
{
int RetVal;
CheckProgramMode (*argv, &ExecProcessingMode);
RetVal = EnvironExecute (argv, 0);
if (ExecProcessingMode.Flags != EP_ENVIRON)
RetVal = LocalExecve (argv, BuildCommandEnvironment (), mode);
if ((RetVal == -1) && (errno == ENOENT))
PrintLoadError (*argv);
return RetVal;
}
/*
* PATH-searching interface to execve.
*/
static int near LocalExecve (char **argv, char **envp, int ForkAction)
{
int res; /* Result */
char *em; /* Exit error message */
int argc = 0; /* Original # of argcs */
char *p_name; /* Program name */
int i;
/* If the environment is null - It is too big - error */
if (envp == (char **)NULL)
em = AE2big;
else if ((p_name = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
em = strerror (ENOMEM);
else
{
/* Start off on the search path for the executable file */
switch (i = FindLocationOfExecutable (p_name, argv[0]))
{
case EXTENSION_EXECUTABLE:
if ((res = ExecuteProgram (p_name, argv, envp, ForkAction))
!= -1)
return res;
break;
/* Script file */
case EXTENSION_BATCH:
case EXTENSION_SHELL_SCRIPT:
if ((res = ExecuteScriptFile (p_name, argv, envp, ForkAction,
(bool)(i == EXTENSION_SHELL_SCRIPT))) != -1)
return res;
break;
}
}
if (errno != ENOENT)
PrintLoadError (*argv);
if (ForkAction & EXEC_WITHOUT_FORK)
exit (-1);
return -1;
}
/*
* Run the command produced by generator `f' applied to stream `arg'.
*/
int RunGeneratorCommand (IO_Args *argp, int (*f)(IO_State *), bool f_loop,
char *name, char **params)
{
Word_B *swdlist = WordListBlock;
Word_B *siolist = IOActionBlock;
jmp_buf ev, rt;
int *ofail = FailReturnPoint;
int RetVal = -1;
Break_C *S_RList = Return_List; /* Save loval links */
Break_C *S_BList = Break_List;
int LS_depth = Execute_stack_depth;
int sjr;
C_Op *outtree;
bool s_ProcessingEXECCommand = ProcessingEXECCommand;
SaveParameters s_Parameters;
/* Create a new save area */
MemoryAreaLevel++;
/* Set up $0..$n for the command if appropriate */
if (params != (char **)NULL)
SaveNumericParameters (params, &s_Parameters);
/* Execute the command */
if (CreateNewEnvironment (setjmp (ErrorReturnPoint = ev)) == FALSE)
{
Return_List = (Break_C *)NULL;
Break_List = (Break_C *)NULL;
WordListBlock = (Word_B *)NULL;
IOActionBlock = (Word_B *)NULL;
AddToIOStack (argp, f, name);
e.iobase = e.iop;
e.eof_p = (bool)!f_loop; /* Set EOF processing */
SW_intr = 0;
AllowMultipleLines = 0;
InParser = FALSE;
ProcessingEXECCommand = (!f_loop) ? TRUE : ProcessingEXECCommand;
/* Read Input (if f_loop is not set, we are processing a . file command)
* either for one line or until end of file.
*/
do
{
if (((sjr = setjmp (FailReturnPoint = rt)) == 0) &&
((outtree = BuildParseTree ()) != (C_Op *)NULL))
RetVal = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
/* Fail or no loop - zap any files if necessary */
else if (sjr || f_loop)
{
ClearExtendedLineFile ();
break;
}
} while (!f_loop);
QuitCurrentEnvironment ();
}
/* Restore the environment */
Return_List = S_RList;
Break_List = S_BList;
ProcessingEXECCommand = s_ProcessingEXECCommand;
WordListBlock = swdlist;
IOActionBlock = siolist;
FailReturnPoint = ofail;
/* Restore $0..$n */
if (params != (char **)NULL)
RestoreTheParameters (&s_Parameters);
RestoreEnvironment (RetVal, LS_depth);
ReleaseMemoryArea (MemoryAreaLevel--);
return RetVal;
}
/* Exec or spawn the program ? */
static int near ExecuteProgram (char *path, char **parms, char **envp,
int ForkAction)
{
int res;
#ifndef OS2
char *ep;
unsigned int size = 0;
int serrno;
unsigned int c_cur = (unsigned int)(_psp - 1);
struct MCB_list *mp = (struct MCB_list *)((unsigned long)c_cur << 16L);
#endif
/* Check to see if the file exists */
strcpy (path_line, path);
/* Check we have access to the file */
if (access (CheckDOSFileName (path_line), F_OK) != 0)
return SetCommandReturnStatus (-1);
/* Process the command line. If no swapping, we have executed the program */
res = BuildCommandLine (path_line, parms, envp, ForkAction);
#ifdef OS2
SetWindowName ();
ClearExtendedLineFile ();
return SetCommandReturnStatus (res);
#else
if ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF) ||
res || (ForkAction & EXEC_WITHOUT_FORK))
{
ClearExtendedLineFile ();
return SetCommandReturnStatus (res);
}
/* Find the length of the swap area */
while ((mp = (struct MCB_list *)((unsigned long)c_cur << 16L))->MCB_type
== MCB_CON)
{
if (c_cur >= 0x9ffe)
break;
if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
(mp->MCB_type != MCB_END))
{
ClearExtendedLineFile ();
PrintErrorMessage ("Fatal: Memory chain corrupt\n");
return SetCommandReturnStatus (-1);
}
c_cur += (mp->MCB_len + 1);
size += mp->MCB_len + 1;
}
/*
* Convert swap size from paragraphs to 16K blocks.
*/
if (size == 0)
size = mp->MCB_len + 1;
SW_Blocks = (size / 0x0400) + 1;
SW_SBlocks = ((size - etext + _psp - 1) / 0x0400) + 1;
/* OK Now we've set up the FCB's, command line and opened the swap file.
* Get some sys info for the swapper and execute my little assembler
* function to swap us out
*/
/* Ok - 3 methods of swapping */
/* If expanded memory - try that */
if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ())
{
SW_Mode = 3; /* Set Expanded memory swap */
if ((res = SwapToMemory (SWAP_EXPAND, envp)) != -2)
return res;
}
if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ())
{
SW_Mode = (SW_fp == -1) ? 2 : 4;/* Set Extended memory or XMS driver */
if ((res = SwapToMemory (SWAP_EXTEND, envp)) != -2)
return res;
}
/* Try the disk if available */
if (Swap_Mode & SWAP_DISK)
{
SW_Pwrite = 0;
if (Swap_File == (char *)NULL)
SW_fp = S_open (FALSE, (ep = GenerateTemporaryFileName ()),
O_SMASK, 0600);
else
{
SW_fp = S_open (FALSE, Swap_File, O_SaMASK);
SW_Pwrite = 1;
}
if (SW_fp < 0)
return SwapToDiskError (ENOSPC, NoSwapFiles);
/* Save the swap file name ? */
if ((Swap_File == (char *)NULL) &&
((Swap_File = StringSave (ep)) == null))
Swap_File = (char *)NULL;
SW_Mode = 1; /* Set Disk file swap */
/* Seek to correct location */
if (SW_Pwrite)
{
long loc = (long)(etext - _psp + 1) * 16L;
if (lseek (SW_fp, loc, SEEK_SET) != loc)
return SwapToDiskError (ENOSPC, NoSwapFiles);
}
/* Execute the program */
res = SpawnProcess (envp);
/* Close the extended command line file */
ClearExtendedLineFile ();
/* Check for out of swap space */
if (res == -2)
return SwapToDiskError (errno, "Swap file write failed\n");
/* Close the swap file */
serrno = errno;
S_close (SW_fp, TRUE);
errno = serrno;
/* Return the result */
return SetCommandReturnStatus (res);
}
/* No swapping available - give up */
ClearExtendedLineFile ();
PrintErrorMessage ("All Swapping methods failed\n");
errno = ENOSPC;
return SetCommandReturnStatus (-1);
#endif
}
#ifndef OS2
/*
* OS2 does not require swapping
*
* Get the XMS Driver information
*/
static bool near Get_XMS_Driver (void)
{
union REGS or;
struct SREGS sr;
unsigned int SW_EMsize; /* Number of extend memory blks */
/* Get max Extended memory pages, and convert to 16K blocks. If Extended
* memory swapping disabled, set to zero
*/
SW_fp = -1; /* Set EMS/XMS handler not */
/* defined */
/* Is a XMS memory driver installed */
or.x.ax = 0x4300;
int86 (0x2f, &or, &or);
if (or.h.al != 0x80)
{
or.x.ax = 0x8800;
int86 (0x15, &or, &or);
SW_EMsize = or.x.ax / 16;
if ((SW_EMsize <= SW_Blocks) ||
(((long)(SW_EMstart - 0x100000L) +
((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
return XMS_error (MS_Space, 0);
else
return TRUE;
}
/* Get the driver interface */
or.x.ax = 0x4310;
int86x (0x2f, &or, &or, &sr);
SW_XMS_Driver = (void (*)())((unsigned long)(sr.es) << 16L | or.x.bx);
/* Support for version 3 of XMS driver */
if ((SW_XMS_Gversion () & 0xff00) < 0x0200)
return FL_TEST ('w') ? XMS_error ("Warning: %s Version < 2\n", 0)
: FALSE;
else if (SW_XMS_Available () < (SW_Blocks * 16))
return FL_TEST ('w') ? XMS_error (MS_Space, 0) : FALSE;
else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1)
return XMS_error (MS_emsg, errno);
return TRUE;
}
/* Get the EMS Driver information */
static bool near Get_EMS_Driver (void)
{
union REGS or;
struct SREGS sr;
char *sp;
/* Set EMS/XMS handler not defined */
SW_fp = -1;
or.x.ax = 0x3567;
intdosx (&or, &or, &sr);
sp = (char *)((unsigned long)(sr.es) << 16L | 10L);
/* If not there - disable */
if (memcmp ("EMMXXXX0", sp, 8) != 0)
return FL_TEST ('w') ? EMS_error ("Warning: %s not available\n", 0)
: FALSE;
or.h.ah = 0x40; /* Check status */
int86 (0x67, &or, &or);
if (or.h.ah != 0)
return EMS_error (MS_emsg, or.h.ah);
/* Check version greater than 3.2 */
or.h.ah = 0x46;
int86 (0x67, &or, &or);
if ((or.h.ah != 0) || (or.h.al < 0x32))
return FL_TEST ('w') ? EMS_error ("Warning: %s Version < 3.2\n", 0)
: FALSE;
/* get page frame address */
or.h.ah = 0x41;
int86 (0x67, &or, &or);
if (or.h.ah != 0)
return EMS_error (MS_emsg, or.h.ah);
SW_EMSFrame = or.x.bx; /* Save the page frame */
/* Get the number of pages required */
or.h.ah = 0x43;
or.x.bx = SW_Blocks;
int86 (0x67, &or, &or);
if (or.h.ah == 0x088)
return EMS_error (MS_Space, 0);
if (or.h.ah != 0)
return EMS_error (MS_emsg, or.h.ah);
/* Save the EMS Handler */
SW_fp = or.x.dx;
/* save EMS page map */
or.h.ah = 0x47;
or.x.dx = SW_fp;
int86 (0x67, &or, &or);
return (or.h.ah != 0) ? EMS_error (MS_emsg, or.h.ah) : TRUE;
}
/* Print EMS error message */
static bool near EMS_error (char *s, int v)
{
PrintWarningMessage (s, "EMS", v);
Swap_Mode &= ~(SWAP_EXPAND);
EMS_Close ();
return FALSE;
}
/* Print XMS error message */
static bool near XMS_error (char *s, int v)
{
PrintWarningMessage (s, "XMS", v);
Swap_Mode &= ~(SWAP_EXTEND);
XMS_Close ();
return FALSE;
}
/* If the XMS handler is defined - close it */
static int near XMS_Close (void)
{
int res = 0;
/* Release XMS page */
if (SW_fp != -1)
res = SW_XMS_Free (SW_fp);
SW_fp = -1;
return res;
}
/* If the EMS handler is defined - close it */
static int near EMS_Close (void)
{
union REGS or;
int res = 0;
if (SW_fp == -1)
return 0;
/* Restore EMS page */
or.h.ah = 0x48;
or.x.dx = SW_fp;
int86 (0x67, &or, &or);
if (or.h.ah != 0)
res = or.h.al;
or.h.ah = 0x45;
or.x.dx = SW_fp;
int86 (0x67, &or, &or);
SW_fp = -1;
return (res) ? res : or.h.ah;
}
#endif
/* Set up command line. If the EXTENDED_LINE variable is set, we create
* a temporary file, write the argument list (one entry per line) to the
* this file and set the command line to @<filename>. If NOSWAPPING, we
* execute the program because I have to modify the argument line
*/
static int near BuildCommandLine (char *path, char **argv, char **envp,
int ForkAction)
{
char **pl = argv;
int res, fd;
bool found;
char *new_args[3];
#ifdef OS2
char cmd_line[NAME_MAX + PATH_MAX + 3];
#endif
/* Translate process name to MSDOS format */
if (GenerateFullExecutablePath (path) == (char *)NULL)
return -1;
/* Extended command line processing */
Extend_file = (char *)NULL; /* Set no file */
found = ((ExecProcessingMode.Flags & EP_UNIXMODE) ||
(ExecProcessingMode.Flags & EP_DOSMODE)) ? TRUE : FALSE;
/* Set up a blank command line */
res = 0;
cmd_line[0] = 0;
cmd_line[1] = CHAR_RETURN;
/* If there are no parameters, or they fit in the DOS command line
* - start the process */
if ((*(++pl) == (char *)NULL) || CheckParameterLength (pl))
return StartTheProcess (path, argv, envp, ForkAction);
/* If we can use an alternative approach - indirect files, use it */
else if (found)
{
char **pl1 = pl;
/* Check parameters don't contain a re-direction parameter */
while (*pl1 != (char *)NULL)
{
if (**(pl1++) == '@')
{
found = FALSE;
break;
}
}
/* If we find it - create a temporary file and write the stuff */
if ((found) &&
((fd = S_open (FALSE,
Extend_file = GenerateTemporaryFileName (), O_CMASK,
0600)) >= 0))
{
if ((Extend_file = StringSave (Extend_file)) == null)
Extend_file = (char *)NULL;
/* Copy to end of list */
while (*pl != (char *)NULL)
{
if (!WriteToExtendedFile (fd, *(pl++)))
return -1;
}
/* Completed write OK */
close (fd);
/* Set up cmd_line[1] to contain the filename */
#ifdef OS2
memset (cmd_line, 0, NAME_MAX + PATH_MAX + 3);
#else
memset (cmd_line, 0, CMD_LINE_MAX);
#endif
cmd_line[1] = CHAR_SPACE;
cmd_line[2] = '@';
strcpy (&cmd_line[3], Extend_file);
cmd_line[0] = (char)(strlen (Extend_file) + 2);
/* Correctly terminate cmd_line in no swap mode */
#ifndef OS2
if (!(ExecProcessingMode.Flags & EP_NOSWAP) &&
(Swap_Mode != SWAP_OFF))
cmd_line[cmd_line[0] + 2] = CHAR_RETURN;
#endif
/* If the name in the file is in upper case - use \ for separators */
if (ExecProcessingMode.Flags & EP_DOSMODE)
PATH_TO_DOS (&cmd_line[2]);
/* OK we are ready to execute */
#ifndef OS2
if ((ExecProcessingMode.Flags & EP_NOSWAP) ||
(Swap_Mode == SWAP_OFF) || (ForkAction & EXEC_WITHOUT_FORK))
{
#endif
new_args[0] = *argv;
new_args[1] = &cmd_line[2];
new_args[2] = (char *)NULL;
return StartTheProcess (path, new_args, envp, ForkAction);
#ifndef OS2
}
else
return 0;
#endif
}
}
return -1;
}
/*
* Clear Extended command line file
*/
void ClearExtendedLineFile (void)
{
if (Extend_file != (char *)NULL)
{
unlink (Extend_file);
ReleaseMemoryCell ((void *)Extend_file);
}
Extend_file = (char *)NULL;
}
#ifndef OS2
/*
* Clear Disk swap file file
*/
void ClearSwapFile (void)
{
if (Swap_File != (char *)NULL)
{
unlink (Swap_File);
ReleaseMemoryCell ((void *)Swap_File);
}
Swap_File = (char *)NULL;
}
#endif
/*
* Convert the executable path to the full path name
*/
static char * near GenerateFullExecutablePath (char *path)
{
char cpath[PATH_MAX + 4];
char npath[PATH_MAX + NAME_MAX + 4];
char n1path[PATH_MAX + 4];
char *p;
int drive;
/* Get path in DOS format */
PATH_TO_DOS (path);
#ifndef OS2
strupr (path);
#else
if (!IsHPFSFileSystem (path))
strupr (path);
#endif
/* Get the current path */
getcwd (cpath, PATH_MAX + 3);
strcpy (npath, cpath);
/* In current directory ? */
if ((p = strrchr (path, '\\')) == (char *)NULL)
{
p = path;
/* Check for a:program case */
if (*(p + 1) == ':')
{
p += 2;
/* Get the path of the other drive */
_getdcwd (tolower (*path) - 'a' + 1, npath, PATH_MAX + 3);
}
}
/* In root directory */
else if ((p - path) == 0)
{
++p;
strcpy (npath, RootDirectory);
*npath = *path;
*npath = *cpath;
}
else if (((p - path) == 2) && (*(path + 1) == ':'))
{
++p;
strcpy (npath, RootDirectory);
*npath = *path;
}
/* Find the directory */
else
{
*(p++) = 0;
/* Change to the directory containing the executable */
drive = (*(path + 1) == ':') ? tolower (*path) - 'a' + 1 : 0;
/* Save the current directory on this drive */
_getdcwd (drive, n1path, PATH_MAX + 3);
/* Find the directory we want */
if (chdir (path) < 0)
return (char *)NULL;
_getdcwd (drive, npath, PATH_MAX + 3); /* Save its full name */
chdir (n1path); /* Restore the original */
/* Restore our original directory */
if (chdir (cpath) < 0)
return (char *)NULL;
}
if (npath[strlen (npath) - 1] != '\\')
strcat (npath, "\\");
strcat (npath, p);
return strcpy (path, npath);
}
/*
* Find the number of values to use for a for or select statement
*/
static char ** near FindNumberOfValues (char **wp, int *Count)
{
/* select/for x do...done - use the parameter values. Need to know how many as
* it is not a NULL terminated array
*/
if (wp == (char **)NULL)
{
if ((*Count = ParameterCount) < 0)
*Count = 0;
return ParameterArray + 1;
}
/* select/for x in y do...done - find the start of the variables and
* use them all
*/
while (*wp++ != (char *)NULL)
continue;
/* Save the start and count them */
*Count = CountNumberArguments (wp);
return wp;
}
/*
* Count the number of entries in an array
*/
int CountNumberArguments (char **wp)
{
int Count = 0;
while (*(wp++) != (char *)NULL)
Count++;
return Count;
}
/*
* Write a command string to the extended file
*/
static bool near WriteToExtendedFile (int fd, char *string)
{
char *sp = string;
char *cp = string;
bool WriteOk = TRUE;
int Length;
if (strlen (string))
{
/* Write the string, converting newlines to backslash newline */
while (WriteOk && (cp != (char *)NULL))
{
if ((cp = strchr (sp, '\n')) != (char *)NULL)
*cp = 0;
if ((Length = strlen (sp)) && (write (fd, sp, Length) != Length))
WriteOk = FALSE;
if (WriteOk && (cp != (char *)NULL))
WriteOk = (write (fd, "\\\n", 2) == 2) ? TRUE : FALSE;
sp = cp + 1;
}
}
if (WriteOk && (write (fd, "\n", 1) == 1))
return TRUE;
close (fd);
ClearExtendedLineFile ();
errno = ENOSPC;
return FALSE;
}
/*
* Execute or spawn the process
*/
static int near StartTheProcess (char *path, char **argv, char **envp,
int ForkAction)
{
#ifdef OS2
void (*sig_int)(); /* Interrupt signal */
int RetVal;
USHORT usType;
STARTDATA stdata;
#endif
/* Is this a start session option */
#ifdef OS2
if (SessionControlBlock != (STARTDATA *)NULL)
return StartTheSession (SessionControlBlock, path, argv, envp);
#endif
if (ForkAction & EXEC_WITHOUT_FORK)
#ifdef OS2
return OS2_DosExecProgram (OLD_P_OVERLAY, path, argv, envp,
ExecProcessingMode.Flags);
#else
return execve (path, ProcessSpaceInParameters (argv), envp);
#endif
#ifndef OS2
return ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF))
? spawnve (P_WAIT, path, ProcessSpaceInParameters (argv), envp)
: 0;
#else
if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |
EXEC_SPAWN_IGNOREWAIT))
{
int Mode = (ForkAction & EXEC_SPAWN_DETACH)
? P_DETACH
: ((ForkAction & EXEC_SPAWN_IGNOREWAIT)
? P_NOWAITO
: P_NOWAIT);
sig_int = signal (SIGINT, SIG_IGN);
RetVal = OS2_DosExecProgram (Mode, path, argv, envp,
ExecProcessingMode.Flags);
signal (SIGINT, sig_int);
/* Remove the reference to the temporary file for background tasks */
ReleaseMemoryCell ((void *)Extend_file);
Extend_file = (char *)NULL;
if ((RetVal != -1) && (Mode != P_NOWAITO))
{
if (Interactive ())
fprintf (stderr, (Mode == P_DETACH)
? "[%d] Process %d detached\n" : "[%d] %d\n",
AddNewJob (RetVal, path), RetVal);
SetVariableFromNumeric ("!", RetVal);
RetVal = 0;
}
}
/* In OS/2, we need the type of the program because PM programs have to be
* started in a session (or at least that was the only way I could get them
* to work).
*/
else if (DosQAppType (path, &usType))
{
errno = ENOENT;
return -1;
}
/* In OS/2, need to set signal to default so child will process it */
else
{
if (Interactive ())
sig_int = signal (SIGINT, SIG_DFL);
if ((usType & 3) == WINDOWAPI)
{
stdata.Length = sizeof (STARTDATA);
stdata.Related = FALSE;
stdata.FgBg = FALSE;
stdata.TraceOpt = 0;
stdata.PgmTitle = (char *)NULL;
stdata.TermQ = 0;
stdata.Environment = (char *)1; /* Build Env */
stdata.InheritOpt = 0;
stdata.SessionType = 3;
stdata.IconFile = (char *)NULL;
stdata.PgmHandle = 0L;
stdata.PgmControl = 8;
stdata.InitXPos = 0;
stdata.InitYPos = 0;
stdata.InitXSize = 100;
stdata.InitYSize = 100;
RetVal = StartTheSession (&stdata, path, argv, envp);
}
else
RetVal = OS2_DosExecProgram (P_WAIT, path, argv, envp,
ExecProcessingMode.Flags);
if (Interactive ())
signal (SIGINT, sig_int);
}
return RetVal;
#endif
}
#ifdef OS2
static int near StartTheSession (STARTDATA *SessionData, char *path,
char **argv, char **envp)
{
USHORT usType;
USHORT idSession;
USHORT pid;
/* Ensure we always start a PM session in PM */
if (DosQAppType (path, &usType))
{
errno = ENOENT;
return -1;
}
if ((usType & 3) == WINDOWAPI)
SessionData->SessionType = 3;
SessionData->PgmName = path;
if ((SessionData->Environment != (char *)NULL) &&
((SessionData->Environment = BuildOS2String (envp, 0)) == (char *)NULL))
return -1;
ProcessSpaceInParameters (argv);
if ((SessionData->PgmInputs = BuildOS2String (&argv[1], 0)) == (char *)NULL)
return -1;
if (!(usType = DosStartSession (SessionData, &idSession, &pid)))
{
fprintf (stderr, "Session %d started\n", (int)idSession);
SetVariableFromNumeric ("!", idSession);
return 0;
}
else
{
errno = ENOENT;
return -1;
}
}
#endif
/*
* Build the OS2 format <value>\0<value>\0 etc \0
*/
static char * near BuildOS2String (char **Array, char sep)
{
int i = 0;
int Length = 0;
char *Output;
char *sp, *cp;
while ((sp = Array[i++]) != (char *)NULL)
Length += strlen (sp) + 1;
Length += 2;
if ((Output = AllocateMemoryCell (Length)) == (char *)NULL)
return (char *)NULL;
i = 0;
sp = Output;
/* Build the string */
while ((cp = Array[i++]) != (char *)NULL)
{
while (*sp = *(cp++))
++sp;
if (!sep || (Array[i] != (char *)NULL))
*(sp++) = sep;
}
*sp = 0;
return Output;
}
/*
* Find the location of an executable and return it's full path
* name
*/
static char *Extensions [] = { null, EXEExtension, COMExtension,
SHELLExtension, BATExtension, };
int FindLocationOfExecutable (char *FullPath, char *name)
{
register char *sp; /* Path pointers */
char *ep;
char *xp; /* In file name pointers */
char *xp1;
int i, fp;
/* Scan the path for an executable */
sp = (any (CHAR_UNIX_DIRECTORY, name) ||
(*(name + 1) == ':')) ? null
: GetVariableAsString (PathLiteral, FALSE);
do
{
sp = BuildNextFullPathName (sp, name, FullPath);
ep = &FullPath[strlen (FullPath)];
/* Get start of file name */
if ((xp1 = strrchr (FullPath, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
xp1 = FullPath;
else
++xp1;
/* Look up all 5 types */
for (i = 0; i < 5; i++)
{
strcpy (ep, Extensions[i]);
if (access (CheckDOSFileName (FullPath), F_OK) == 0)
{
/* If no extension or .sh extension, check for shell script */
if (((xp = strchr (xp1, '.')) == (char *)NULL) ||
(stricmp (xp, SHELLExtension) == 0))
{
if ((fp = CheckForScriptFile (FullPath, (char **)NULL,
(int *)NULL)) < 0)
continue;
S_close (fp, TRUE);
return EXTENSION_SHELL_SCRIPT;
}
else if (!stricmp (xp, EXEExtension) ||
!stricmp (xp, COMExtension))
return EXTENSION_EXECUTABLE;
else if (!stricmp (xp, BATExtension))
return EXTENSION_BATCH;
}
}
} while (sp != (char *)NULL);
/* Not found */
errno = ENOENT;
return EXTENSION_NOT_FOUND;
}
/*
* Execute a script file
*/
static int near ExecuteScriptFile (char *Fullpath, char **argv, char **envp,
int ForkAction, bool ShellScript)
{
register char *sp;
int res; /* Result */
char *params; /* Script parameters */
int nargc = 0; /* # script args */
Word_B *wb = (Word_B *)NULL;
int j;
char **nargv;
#ifndef OS2
union REGS r;
#endif
/* Batfile - convert to DOS Format file name */
if (!ShellScript)
{
PATH_TO_DOS (Fullpath);
nargc = 0;
}
else if ((res = OpenForExecution (Fullpath, ¶ms, &nargc)) >= 0)
S_close (res, TRUE);
else
{
errno = ENOENT;
return -1;
}
/* If BAT file, use command.com else use sh */
if (!ShellScript)
{
if ((sp = StringCopy (GetVariableAsString (ComspecVariable,
FALSE))) == null)
return -1;
wb = SplitString (sp, wb);
wb = AddWordToBlock ("/c", wb);
#ifndef OS2
/* Get the switch character */
r.x.ax = 0x3700;
intdos (&r, &r);
if ((r.h.al == 0) && (_osmajor < 4))
*wb->w_words[1] = (char)(r.h.dl);
#endif
}
/* Stick in the pre-fix arguments */
else if (nargc)
wb = SplitString (params, wb);
else if (params != null)
wb = AddWordToBlock (params, wb);
else
wb = AddWordToBlock (GetVariableAsString (ShellVariableName, FALSE),
wb);
/* Add the rest of the parameters */
wb = AddWordToBlock (Fullpath, wb);
j = 1;
while (argv[j] != (char *)NULL)
wb = AddWordToBlock (argv[j++], wb);
/* Execute the program */
nargv = GetWordList (AddWordToBlock (NOWORD, wb));
/* Save the old empty space value and use the max */
CheckProgramMode (*nargv, &ExecProcessingMode);
/* Special to try and sort out command.com. The only flag we don't carry over
* from the original command is the CONVERT flag. Not that it makes much
* difference, since it has already been processed.
*/
if (!ShellScript)
ExecProcessingMode.Flags &= ~EP_CONVERT;
res = EnvironExecute (nargv, ForkAction);
if (ExecProcessingMode.Flags != EP_ENVIRON)
res = LocalExecve (nargv, envp, ForkAction);
/* Release allocated space */
if (params != null)
ReleaseMemoryCell ((void *)params);
/* 0 is a special case - see ConvertErrorNumber */
if (res == -1)
errno = 0;
return res;
}
/*
* Convert errno to error message on execute
*/
static char * near ConvertErrorNumber (void)
{
switch (errno)
{
case ENOMEM:
return strerror (ENOMEM);
case ENOEXEC:
return "program corrupt";
case E2BIG:
return AE2big;
case ENOENT:
return NotFound;
case 0:
return "No Shell";
}
return "cannot execute";
}
#ifndef OS2
/*
* Swap to disk error
*/
static int near SwapToDiskError (int error, char *ErrorMessage)
{
/* Close the swap file, if open */
if (SW_fp >= 0)
S_close (SW_fp, TRUE);
/* Clean up */
ClearSwapFile ();
Swap_Mode &= (~SWAP_DISK);
PrintErrorMessage (ErrorMessage);
errno = error;
return SetCommandReturnStatus (-1);
}
/*
* Swap to memory
*/
static int near SwapToMemory (int mode, char **envp)
{
int res;
int cr;
/* Swap and close memory handler */
res = SpawnProcess (envp);
cr = (SW_Mode != 3) ? XMS_Close () : EMS_Close ();
if ((res != -2) && cr) /* Report Close error ? */
{
res = -2;
errno = cr;
}
if (res == -2)
(SW_Mode != 3) ? XMS_error (SwapFailed, errno)
: EMS_error (SwapFailed, errno);
else
{
ClearExtendedLineFile ();
return SetCommandReturnStatus (res);
}
/* Failed - disabled */
Swap_Mode &= (~mode);
return res;
}
#endif
/*
* Check the program type
*/
void CheckProgramMode (char *Pname, struct ExecutableProcessing *PMode)
{
char *sp, *sp1; /* Line pointers */
int nFields;
char *SPname;
int builtin; /* Builtin function */
LineFields LF;
long value;
/* Check for internal no-globbed commands */
if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)())NULL) &&
((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB))
{
PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);
return;
}
/* Set not found */
PMode->Flags = EP_NONE;
/* Check not a function */
if ((Pname == (char *)NULL) ||
((sp = GetVariableAsString ("EXTENDED_LINE", FALSE)) == null))
return;
/* Get some memory for the input line and the file name */
sp1 = ((sp1 = strrchr (Pname, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
? Pname : sp1 + 1;
if (*(sp1 + 1) == ':')
sp1 += 2;
if ((SPname = StringCopy (sp1)) == null)
return;
if ((LF.Line = AllocateMemoryCell (LF.LineLength = 200)) == (char *)NULL)
{
ReleaseMemoryCell ((void *)SPname);
return;
}
/* Remove terminating .exe etc */
if ((sp1 = strrchr (SPname, '.')) != (char *)NULL)
*sp1 = 0;
/* Open the file */
if ((LF.FP = fopen (sp, "rt")) == (FILE *)NULL)
{
ReleaseMemoryCell ((void *)LF.Line);
ReleaseMemoryCell ((void *)SPname);
return;
}
/* Initialise the internal buffer */
LF.Fields = (Word_B *)NULL;
/* Scan for the file name */
while ((nFields = ExtractFieldsFromLine (&LF)) != -1)
{
if (nFields < 2)
continue;
/* Remove terminating .exe etc */
if ((sp = strrchr (LF.Fields->w_words[0], '.')) != (char *)NULL)
*sp = 0;
if (stricmp (LF.Fields->w_words[0], SPname))
continue;
/* What type? */
if (stricmp (LF.Fields->w_words[1], "unix") == 0)
PMode->Flags = (unsigned int )(EP_UNIXMODE |
CheckForCommonOptions (&LF, 2));
else if (stricmp (LF.Fields->w_words[1], "dos") == 0)
PMode->Flags = (unsigned int )(EP_DOSMODE |
CheckForCommonOptions (&LF, 2));
/* Must have a valid name and we can get memory for it */
else if ((stricmp (LF.Fields->w_words[1], "environ") == 0) &&
(nFields >= 3) &&
(!IsValidVariableName (LF.Fields->w_words[2])) &&
((PMode->Name =
StringCopy (LF.Fields->w_words[2])) != null))
{
PMode->Flags = EP_ENVIRON;
PMode->FieldSep = 0;
if ((nFields >= 4) &&
ConvertNumericValue (LF.Fields->w_words[3], &value, 0))
PMode->FieldSep = (unsigned char)value;
if (!PMode->FieldSep)
PMode->FieldSep = ' ';
}
else
PMode->Flags = CheckForCommonOptions (&LF, 1);
break;
}
fclose (LF.FP);
ReleaseMemoryCell ((void *)LF.Line);
ReleaseMemoryCell ((void *)SPname);
}
/*
* Check for common fields
*/
static unsigned int near CheckForCommonOptions (LineFields *LF, int Start)
{
unsigned int Flags = 0;
int i, j;
if (LF->Fields == (Word_B *)NULL)
return 0;
for (i = Start; i < LF->Fields->w_nword; i++)
{
for (j = 0; j < COMMON_FIELD_COUNT; ++j)
{
if (!stricmp (LF->Fields->w_words[i], CommonFields[j].Name))
{
Flags |= CommonFields[j].Flag;
break;
}
}
}
return Flags;
}
/*
* Convert UNIX format lines to DOS format if appropriate.
* Build Environment variable for some programs.
*/
static int near EnvironExecute (char **argv, int ForkAction)
{
int s_errno;
int RetVal = 1;
char *NewArgs[3];
char *cp;
/* If this command does not pass the command string in the environment,
* no action required
*/
if (ExecProcessingMode.Flags != EP_ENVIRON)
return 0;
if ((cp = BuildOS2String (&argv[1], ExecProcessingMode.FieldSep))
== (char *)NULL)
{
ExecProcessingMode.Flags = EP_NONE;
return 0;
}
SetVariableFromString (ExecProcessingMode.Name, cp);
SetVariableStatus (ExecProcessingMode.Name, STATUS_EXPORT);
/* Build and execute the environment */
NewArgs[0] = argv[0];
NewArgs[1] = ExecProcessingMode.Name;
NewArgs[2] = (char *)NULL;
RetVal = LocalExecve (NewArgs, BuildCommandEnvironment (), ForkAction);
s_errno = errno;
UnSetVariable (ExecProcessingMode.Name, FALSE);
errno = s_errno;
return RetVal;
}
/*
* Set Interrupt handling vectors - moved from sh0.asm
*/
#ifndef OS2
static int near SpawnProcess (char **envp)
{
void (interrupt far *SW_I00_V) (void); /* Int 00 address */
void (interrupt far *SW_I23_V) (void); /* Int 23 address*/
int res;
#if 0
union REGS r;
unsigned char Save;
r.x.ax = 0x3300;
intdos (&r, &r);
Save = r.h.al;
fprintf (stderr, "Break Status: %s (%u)\n", Save ? "on" : "off", Save);
r.x.ax = 0x3301;
r.h.dl = 1;
intdos (&r, &r);
fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
#endif
/*
* Save current vectors
*/
SW_I00_V = _dos_getvect (0x00);
SW_I23_V = _dos_getvect (0x23);
/*
* Set In shell flag for Interrupt 23, and set to new interrupts
*/
SW_I23_InShell = 0;
_dos_setvect (0x23, SW_Int23);
_dos_setvect (0x00, SW_Int00);
res = SA_spawn (envp);
/*
* Restore interrupt vectors
*/
_dos_setvect (0x00, SW_I00_V);
_dos_setvect (0x23, SW_I23_V);
#if 0
r.x.ax = 0x3300;
intdos (&r, &r);
fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
r.x.ax = 0x3301;
r.h.dl = Save;
intdos (&r, &r);
#endif
/*
* Check for an interrupt
*/
if (SW_intr)
raise (SIGINT);
return res;
}
#endif
/*
* Check Parameter line length
*
* Under OS2, we don't build the command line. Just check it.
*/
static bool near CheckParameterLength (char **argv)
{
int CmdLineLength;
char *CommandLine;
bool RetVal;
char **SavedArgs = argv;
/* Check for special case. If there are any special characters and we can
* use UNIX mode, use it
*/
if (ExecProcessingMode.Flags & EP_UNIXMODE)
{
while (*argv != (char *)NULL)
{
if (anys ("\"'", *(argv++)))
return FALSE;
}
}
/*
* Do any parameter conversion - adding quotes or backslashes, but don't
* update argv.
*/
CmdLineLength = CountNumberArguments (argv = SavedArgs);
if ((SavedArgs = (char **)
AllocateMemoryCell ((CmdLineLength + 1) * sizeof (char *)))
== (char *)NULL)
return FALSE;
/* Save a copy of the argument addresses */
memcpy (SavedArgs, argv, (CmdLineLength + 1) * sizeof (char *));
/* Build the command line */
if ((CommandLine = BuildOS2String (ProcessSpaceInParameters (SavedArgs),
CHAR_SPACE)) == (char *)NULL)
{
ReleaseMemoryCell (SavedArgs);
return FALSE;
}
/* Check command line length */
if ((CmdLineLength = strlen (CommandLine)) >= CMD_LINE_MAX - 2)
{
errno = E2BIG;
RetVal = FALSE;
}
/* Terminate the line */
else
{
#ifndef OS2
strcpy (cmd_line + 1, CommandLine);
cmd_line[CmdLineLength + 1] = CHAR_RETURN;
cmd_line[0] = (char)CmdLineLength;
#endif
RetVal = TRUE;
}
ReleaseMemoryCell (SavedArgs);
ReleaseMemoryCell (CommandLine);
return RetVal;
}
/*
* Convert any parameters with spaces in the to start and end with double
* quotes.
*
* Under OS2, the old string is NOT released.
*/
static char **ProcessSpaceInParameters (char **argv)
{
char **Start = argv;
char *new;
char *cp;
char *sp;
int Count;
/* Protect parameters with TABS */
while (*argv != (char *)NULL)
{
if ((strchr (*argv, CHAR_SPACE) != (char *)NULL) ||
(strchr (*argv, CHAR_TAB) != (char *)NULL) ||
(strlen (*argv) == 0))
{
/* Count number of Double quotes in the parameter */
Count = CountDoubleQuotes (*argv);
/* Get some memory - give up update if out of memory */
if ((new = GetAllocatedSpace (strlen (*argv) + (Count * 2) +
3)) == (char *)NULL)
return Start;
SetMemoryAreaNumber ((void *)new,
GetMemoryAreaNumber ((void *)*argv));
*new = CHAR_DOUBLE_QUOTE;
/* Escape any double quotes in the string */
cp = *argv;
sp = new + 1;
while (*cp)
{
if (*cp == CHAR_DOUBLE_QUOTE)
{
*(sp++) = '\\';
*(sp++) = *(cp++);
}
else if (*cp != '\\')
*(sp++) = *(cp++);
/* Handle escapes - count them */
else
{
*(sp++) = *(cp++);
if (*cp == CHAR_DOUBLE_QUOTE)
{
*(sp++) = '\\';
*(sp++) = '\\';
}
else if (*cp == 0)
{
*(sp++) = '\\';
break;
}
*(sp++) = *(cp++);
}
}
/* Append the terminating double quotes */
strcpy (sp, DoubleQuotes);
*argv = new;
}
/* Check for any double quotes */
else if (Count = CountDoubleQuotes (*argv))
{
/* Got them - escape them */
if ((new = GetAllocatedSpace (strlen (*argv) + Count + 1))
== (char *)NULL)
return Start;
SetMemoryAreaNumber ((void *)new,
GetMemoryAreaNumber ((void *)*argv));
/* Copy the string, escaping DoubleQuotes */
cp = *argv;
sp = new;
while (*sp = *(cp++))
{
if (*sp == CHAR_DOUBLE_QUOTE)
{
*(sp++) = '\\';
*sp = CHAR_DOUBLE_QUOTE;
}
sp++;
}
*argv = new;
}
/* Next parameter */
argv++;
}
return Start;
}
/*
* Count DoubleQuotes
*/
static int near CountDoubleQuotes (char *string)
{
int Count = 0;
while ((string = strchr (string, CHAR_DOUBLE_QUOTE)) != (char *)NULL)
{
Count++;
string++;
}
return Count;
}
/*
* Save and Restore the Parameters array ($1, $2 etc)
*/
static void near SaveNumericParameters (char **wp, SaveParameters *SaveArea)
{
SaveArea->Array = ParameterArray;
SaveArea->Count = ParameterCount;
ParameterArray = wp;
for (ParameterCount = 0;
ParameterArray[ParameterCount] != (char *)NULL; ++ParameterCount)
continue;
SetVariableFromNumeric (ParameterCountVariable, (long)--ParameterCount);
}
static void near RestoreTheParameters (SaveParameters *SaveArea)
{
ParameterArray = SaveArea->Array;
ParameterCount = SaveArea->Count;
SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);
}
#ifdef OS2
/*
* Special OS/2 processing for execve and spawnve
*/
static int near OS2_DosExecProgram (int Mode, char *Program, char **argv,
char **envp, unsigned int Type)
{
USHORT fExecFlags;
RESULTCODES rescResults;
char *OS2Environment;
char *OS2Arguments;
USHORT ErrorCode;
char **SavedArgs;
int argc;
/* Set the error module to null */
*FailName = 0;
/* Convert spawn mode to DosExecPgm mode */
switch (Mode)
{
case P_WAIT:
fExecFlags = EXEC_SYNC;
break;
case P_NOWAIT:
fExecFlags = EXEC_ASYNCRESULT;
break;
case P_NOWAITO:
case OLD_P_OVERLAY:
fExecFlags = EXEC_ASYNC;
break;
case P_DETACH:
fExecFlags = EXEC_BACKGROUND;
break;
}
/* Build OS/2 argument string
*
* 1. Count the number of arguments.
* 2. Add 2 for: 1 - NULL; 2 - argv[0]; 3 - the stringed arguments
* 3. save copy of arguments at offset 2.
* 4. On original argv, process white space and convert to OS2 Argument string.
* 5. Set up program name at offset 2 as ~argv
* 6. Convert zero length args to "~" and args beginning with ~ to ~~
* 7. Build OS2 Argument string (at last).
*/
argc = CountNumberArguments (argv);
if ((SavedArgs = (char **)AllocateMemoryCell ((argc + 3) * sizeof (char *)))
== (char *)NULL)
return -1;
memcpy (SavedArgs + 2, argv, (argc + 1) * sizeof (char *));
/* Set program name at Offset 0 */
SavedArgs[0] = *argv;
/* Build OS2 Argument string in Offset 1 */
if ((SavedArgs[1] = BuildOS2String (ProcessSpaceInParameters (&argv[1]),
CHAR_SPACE)) == (char *)NULL)
return -1;
/* Set up the new arg 2 - ~ + programname */
if ((SavedArgs[2] = InsertCharacterAtStart (*argv)) == (char *)NULL)
return -1;
/* Convert zero length args and args starting with a ~ */
for (argc = 3; SavedArgs[argc] != (char *)NULL; argc++)
{
if (strlen (SavedArgs[argc]) == 0)
SavedArgs[argc] = "~";
else if ((*SavedArgs[argc] == CHAR_TILDE) &&
((SavedArgs[argc] = InsertCharacterAtStart (SavedArgs[argc]))
== (char *)NULL))
return -1;
}
/* Build the full argument list */
if ((OS2Arguments = BuildOS2String (SavedArgs, 0)) == (char *)NULL)
return -1;
/* Build OS/2 environment string */
if ((OS2Environment = BuildOS2String (envp, 0)) == (char *)NULL)
return -1;
/* Exec it */
ErrorCode = DosExecPgm (FailName, sizeof (FailName), fExecFlags,
OS2Arguments,
OS2Environment,
&rescResults, Program);
/*
* What happened ?. OS/2 Error - Map to UNIX errno. Why can't people
* write libraries right?? Or provide access at a high level. We could
* call _dosret if the interface did not require me to write more
* assembler.
*/
if (ErrorCode != 0)
{
switch (ErrorCode)
{
case ERROR_NO_PROC_SLOTS:
errno = EAGAIN;
return -1;
case ERROR_NOT_ENOUGH_MEMORY:
errno = ENOMEM;
return -1;
case ERROR_ACCESS_DENIED:
case ERROR_DRIVE_LOCKED:
case ERROR_LOCK_VIOLATION:
case ERROR_SHARING_VIOLATION:
errno = EACCES;
return -1;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_PROC_NOT_FOUND:
errno = ENOENT;
return -1;
case ERROR_BAD_ENVIRONMENT:
case ERROR_INVALID_DATA:
case ERROR_INVALID_FUNCTION:
case ERROR_INVALID_ORDINAL:
case ERROR_INVALID_SEGMENT_NUMBER:
case ERROR_INVALID_STACKSEG:
case ERROR_INVALID_STARTING_CODESEG:
errno = EINVAL;
return -1;
case ERROR_TOO_MANY_OPEN_FILES:
errno = EMFILE;
return -1;
case ERROR_INTERRUPT:
case ERROR_NOT_DOS_DISK:
case ERROR_SHARING_BUFFER_EXCEEDED:
errno = EIO;
return -1;
default:
errno = ENOEXEC;
return -1;
}
}
/* If exec - exit */
switch (Mode)
{
case OLD_P_OVERLAY: /* exec - exit at once */
while (1)
(void)DosExit (EXIT_PROCESS, 0);
case P_WAIT: /* Get exit code */
return rescResults.codeResult;
case P_NOWAIT: /* Get PID or SID */
case P_NOWAITO:
case P_DETACH:
return rescResults.codeTerminate;
}
errno = EINVAL;
return -1;
}
/*
* Insert character at start of string
*
* Return NULL or new string
*/
static char *InsertCharacterAtStart (char *string)
{
char *cp;
if ((cp = (char *)AllocateMemoryCell (strlen (string) + 2)) == (char *)NULL)
return (char *)NULL;
*cp = CHAR_TILDE;
strcpy (cp + 1, string);
return cp;
}
#endif
/*
* Execute a Function
*/
static bool near ExecuteFunction (char **wp, int *RetVal, bool CGVLCalled)
{
Break_C *s_RList = Return_List;
Break_C *s_BList = Break_List;
Break_C *s_SList = SShell_List;
Break_C BreakContinue;
C_Op *New;
FunctionList *s_CurrentFunction = CurrentFunction;
SaveParameters s_Parameters;
FunctionList *fop;
GetoptsIndex GetoptsSave;
/* Find the function */
if ((fop = LookUpFunction (wp[0])) == (FunctionList *)NULL)
return FALSE;
/* Save the current variable list */
if (!CGVLCalled && (CreateGlobalVariableList (FLAGS_FUNCTION) == -1))
{
*RetVal = -1;
return TRUE;
}
/* Set up $0..$n for the function */
SaveNumericParameters (wp, &s_Parameters);
/* Save Getopts pointers */
GetGetoptsValues (&GetoptsSave);
/* Process the function */
if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
{
CurrentFunction = fop;
Break_List = (Break_C *)NULL;
BreakContinue.NextExitLevel = Return_List;
Return_List = &BreakContinue;
New = CopyFunction (fop->tree->left);
*RetVal = ExecuteParseTree (New, NOPIPE, NOPIPE, EXEC_FUNCTION);
}
/* A return has been executed - Unlike, while and for, we just need to
* restore the local execute stack level and the return will restore
* the correct I/O.
*/
else
*RetVal = (int)GetVariableAsNumeric (StatusVariable);
/* Restore the old $0, and previous return address */
SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
Break_List = s_BList;
Return_List = s_RList;
SShell_List = s_SList;
CurrentFunction = s_CurrentFunction;
RestoreTheParameters (&s_Parameters);
RunTrapCommand (0); /* Exit trap */
return TRUE;
}
/*
* Print Load error message
*/
static void near PrintLoadError (char *path)
{
#ifdef OS2
fprintf (stderr, "%s: ", path);
if (*FailName)
fprintf (stderr, "%s ", FailName);
PrintWarningMessage ("%s\n", ConvertErrorNumber());
#else
PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());
#endif
}